/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.cloudbus.cloudsim.power.models.scheduling;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.cloudbus.cloudsim.Vm;
import org.cloudbus.cloudsim.Cloudlet;
import org.cloudbus.cloudsim.DatacenterBroker;
import org.cloudbus.cloudsim.Log;
import org.cloudbus.cloudsim.core.CloudSimTags;
import org.cloudbus.cloudsim.core.SimEvent;

/**
 *
 * @author Márcio Costa Junior - University of Sao Paulo
 */
public class Middleware extends DatacenterBroker{
    
    private ArrayList<ClassMid> classList;
    private int numberClasses;
    private ArrayList<Vm> vm;
    
    public static int TIMEZONE_FACTOR = 10;
    
    public Middleware(String name, int numberClasses, double mipsBarrier) throws Exception{
        super(name);
        
        setNumberClasses(numberClasses);
        setClassList(new ArrayList<ClassMid>());
        
        int barrier = (int) (mipsBarrier/numberClasses);
        int lastBarrier = 0;
        
        for(int i = 0; i < numberClasses; i++){
            getClassList().add(new ClassMid(lastBarrier, lastBarrier + barrier, i, getId()));
            lastBarrier += barrier;
        }
        
        
        
        
    }
    
    /**
     * Processes events available for this Broker.
     *
     * @param ev    a SimEvent object
     *
     * @pre ev != null
     * @post $none
     */
	@Override
	public void processEvent(SimEvent ev) {
		//Log.printLine(CloudSim.clock()+"[Broker]: event received:"+ev.getTag());
		switch (ev.getTag()){
			// Resource characteristics request
			case CloudSimTags.RESOURCE_CHARACTERISTICS_REQUEST:
				processResourceCharacteristicsRequest(ev);
				break;
			// Resource characteristics answer
	        case CloudSimTags.RESOURCE_CHARACTERISTICS:
	        	processResourceCharacteristics(ev);
	            break;
	        // VM Creation answer
	        case CloudSimTags.VM_CREATE_ACK:
	           	processVmCreate(ev);
	           	break;
	        //A finished cloudlet returned
	        case CloudSimTags.CLOUDLET_RETURN:
	        	processCloudletReturn(ev);
	            break;
	        // if the simulation finishes
	        case CloudSimTags.END_OF_SIMULATION:
	        	shutdownEntity();
	            break;
                
	        case CloudSimTags.SEND_REQUEST_MIDDLEWARE_VM_LIST:
	        	receiveVm(ev.getData());
	            break;
                    
	        case CloudSimTags.SEND_REQUEST_MIDDLEWARE_CL_LIST:
	        	receiveCl(ev.getData());
	            break;                   
            // other unknown tags are processed by this method
	        default:
	            processOtherEvent(ev);
	            break;
		}
	}
       
        
    public void createFrontEnds(int numberHosts, int numberPe){
        
            for(ClassMid group : getClassList()){
                group.createFrontEnd(numberHosts, numberPe);
            }
        
    }
    
    public void createLocalDatacenters(int mips, int ram, long storage, int bw, double timeZone, double peCost, 
            long costPerMem, long costPerStorage, long costPerBw, double pePotency, double powerPercent,
            double processingCost, String os, String arch, String vmm){
        
            for(ClassMid group : getClassList()){
                group.createLocalDatacenter(mips, ram, storage, bw, timeZone, peCost, 
                        costPerMem, costPerStorage, 
                        costPerBw, pePotency, powerPercent, processingCost, os, arch, vmm);
            }
        
    }
    
    public void submitRequestList(List<List<Vm>> vmList, List<List<Cloudlet>> cloudletList){
        
            for(int i = 0; i < getClassList().size(); i++){
                getClassList().get(i).getFrontEnd().submitVmList(vmList.get(i));
                getClassList().get(i).getFrontEnd().submitCloudletList(cloudletList.get(i));
        }
        
    }
    
   
    public void createEstimators(List<List<Vm>> vmList){
        
        for(int i = 0; i < getClassList().size(); i++){
            getClassList().get(i).createEstimator(vmList.get(i).size());
        }
       
    }
    
    private void receiveVm(Object vm){
        this.vm = (ArrayList<Vm>) vm;
    }
    
    private void receiveCl(Object cl){
        
        ArrayList<Cloudlet> clList = (ArrayList<Cloudlet>) cl;
        try {
            //Chaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaange here!!!
            this.getClassList().get(0).getFrontEnd().createVmAndCloudlet(vm, clList);
        } catch (InterruptedException ex) {
            Log.print("It was not possible to send the requests to middleware #" + this.getId());
        }
        
    }
   
     
    public void sendRequestToMiddleware(Middleware mid, ArrayList<Vm> vm, ArrayList<Cloudlet> cloudlet){
        
        double delay = mid.getClassList().get(0).getFrontEnd().gethCarac().getTimeZone() * Middleware.TIMEZONE_FACTOR;
        
        send(mid.getId(), delay, CloudSimTags.SEND_REQUEST_MIDDLEWARE_VM_LIST, vm);
        send(mid.getId(), delay, CloudSimTags.SEND_REQUEST_MIDDLEWARE_CL_LIST, cloudlet);
    }
        
    /**
     * @return the classList
     */
    public ArrayList<ClassMid> getClassList() {
        return classList;
    }

    /**
     * @param classList the classList to set
     */
    public void setClassList(ArrayList<ClassMid> classList) {
        this.classList = classList;
    }

    /**
     * @return the numberClasses
     */
    public int getNumberClasses() {
        return numberClasses;
    }


    /**
     * @param numberClasses the numberClasses to set
     */
    public void setNumberClasses(int numberClasses) {
        this.numberClasses = numberClasses;
    }
    
    
    
    
}
